//
//  EncryptVC.m
//  CodeHelper
//
//  Created by Rowling on 2023/5/8.
//

#import "EncryptVC.h"
#import "StringEncrypt.h"
#import "Scanner.h"
@interface EncryptVC ()<NSOpenSavePanelDelegate>
@property (weak) IBOutlet NSTextField *pathLabel;
@property (weak) IBOutlet NSTextField *keyText;

@property (strong, nonatomic) NSURL * currentEncrpytUrl;
@property (strong, nonatomic) NSTextField * toastLabel;
@property (strong, nonatomic) NSOpenPanel * panel;
@property (assign, nonatomic) NSInteger count;
// key
@property (strong, nonatomic) NSTextView * textView;

@property (strong, nonatomic) Config * config;

@property (weak) IBOutlet NSScrollView *scrollView;

@property (weak) IBOutlet NSTextField *decryptName;

@property (weak) IBOutlet NSComboBox *mode;

@property (weak) IBOutlet NSTextField *ivText;

@property (weak) IBOutlet NSStackView *ivContainer;
@end

@implementation EncryptVC

-(void)setCurrentEncrpytUrl:(NSURL *)currentEncrpytUrl {
    _currentEncrpytUrl = currentEncrpytUrl;
    self.pathLabel.stringValue = [_currentEncrpytUrl path];
}

- (Config *)config {
    NSString * decryptKey = @"decrypt";
    if (![self.decryptName.stringValue isEqualToString:@""]) {
        decryptKey = self.decryptName.stringValue;
    }
    NSString * AESKey = self.keyText.stringValue;
    NSString * markKey = @"MARKER";
    Config * config = [Config new];
    config.AESKey = AESKey;
    config.markKey = markKey;
    config.decryptMethodKey = decryptKey;
    config.markDecryptError = @"DecryptError";
    config.mode = [self.mode.stringValue isEqualToString:@"ECB"] ? 0 : 1;
    config.ivKey = self.ivText.stringValue;
    config.swiftStrMatchRegular = [NSString stringWithFormat:@"(?<!\\))\"(?!\"\"|.*\\.%@\\(\\)|.*\\.autoEncrpytIgnore\\(\\))(.*?)(\\\\\\\\|(?<!\\\\|\"\"))\"(?!\"|\\(|\\.autoEncrpytIgnore\\(\\)|\\.%@\\(\\))", decryptKey, decryptKey];
    
    config.ocStrMatchRegular = [NSString stringWithFormat:@"(?<!MARKER\\()@\".*?(?<!\\\\)\"(?!\\.%@|\\.autoEncrpytIgnore)",
                                decryptKey
    ];
    return  config;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do view setup here.

    self.title = @"AES";
    [self setUI];
    
    NSString * aesKey = [[NSUserDefaults standardUserDefaults] objectForKey:@"aesKey"];
    if (aesKey) {
        self.keyText.stringValue = aesKey;
    }
    
    [self updateUI];
}

-(void)updateUI {
    self.ivContainer.hidden = self.config.mode == 0;
}

-(void)viewDidLayout {
    [super viewDidLayout];
    self.textView.frame = self.scrollView.bounds;
}

-(void)setUI {
    NSRect r = {0, 0, 200, 200};
    NSTextView * text = [[NSTextView alloc] initWithFrame:r];
    
    text.editable = NO;
    
    text.font = [NSFont labelFontOfSize:14];

    self.textView = text;
    
    [self.scrollView setDocumentView:text];

    [self.scrollView setHasVerticalScroller:YES];

    [self.scrollView setHasHorizontalScroller:YES];

    [self.scrollView setBorderType:NSNoBorder];
}


-(void)showToast:(NSString *)content title:(NSString *)title {
    NSAlert * alert = [NSAlert new];
    alert.messageText = title;
    alert.informativeText = content;
    [alert runModal];
}

-(void)showToast:(NSString *)content {
    [self showToast:content title:@"⚠️"];
}

- (IBAction)onChooseFile:(id)sender {
    NSOpenPanel * panel = [NSOpenPanel new];
    panel.message = @"不要随便选择文件";
    panel.delegate = self;
    panel.canChooseDirectories = YES;
    if ([panel runModal]) {
        NSLog(@"%@", panel.URLs);
        self.currentEncrpytUrl = panel.URLs.firstObject;
    }
//    self.panel = [[NSOpenPanel alloc] init];
//    self.panel.canChooseFiles = true;
//    self.panel.canChooseDirectories = true;
//    NSLog(@"%@", [NSThread currentThread]);
//    [self.panel beginWithCompletionHandler:^(NSModalResponse result) {
//        if (result == NSModalResponseOK) {
//            NSError * error;
//            [@"Hello, world!" writeToURL:self.panel.URL atomically:true  encoding:NSUTF8StringEncoding error:&error];
//            if (error) {
//                NSLog(@"%@",error);
//            }
//        }
//    }];
}

- (IBAction)onModeChnage:(NSComboBox *)sender {
    NSLog(@"%@", sender.stringValue);
    [self updateUI];
}


- (IBAction)onMark:(id)sender {
    if (![self checkSelectUrl]) {return;}
    self.count = 0;
    self.textView.string = @"";
    [self handleEncryptFiles:self.currentEncrpytUrl.path mode:0 ignoreDirNames:nil];
    NSString * tips = [NSString stringWithFormat:@"当前标记操作已完成---一共标记 %ld 处", self.count];
    NSLog(@"%@",tips);
    [self showToast:tips];
}

- (BOOL)checkSelectUrl {
    if (self.currentEncrpytUrl == nil){
        [self showToast:@"请选择文件"];
        return NO;
    }
    return  YES;
}

- (IBAction)onEncrpyt:(id)sender {
    if (![self normalOptionCheck]){return;}
    [self handleEncryptFiles:self.currentEncrpytUrl.path mode:1 ignoreDirNames:nil];
    [[NSUserDefaults standardUserDefaults] setObject:self.config.AESKey forKey:@"aesKey"];
    NSString * tips = [NSString stringWithFormat:@"当前加密操作已完成---一共加密 %ld 处", self.count];
    NSLog(@"%@",tips);
    [self showToast:tips];
}

- (IBAction)onDecrypt:(id)sender {
    if (![self normalOptionCheck]){return;}
    [self handleEncryptFiles:self.currentEncrpytUrl.path mode:2 ignoreDirNames:nil];
    [[NSUserDefaults standardUserDefaults] setObject:self.config.AESKey forKey:@"aesKey"];
    NSString * tips = [NSString stringWithFormat:@"当前解密操作已完成---一共解密 %ld 处", self.count];
    NSLog(@"%@",tips);
    [self showToast:tips];
}

- (IBAction)onDecryptWithMarker:(id)sender {
    if (![self normalOptionCheck]){return;}
    [self handleEncryptFiles:self.currentEncrpytUrl.path mode:3 ignoreDirNames:nil];
    [[NSUserDefaults standardUserDefaults] setObject:self.config.AESKey forKey:@"aesKey"];
    NSString * tips = [NSString stringWithFormat:@"当前解密操作已完成---一共解密 %ld 处", self.count];
    NSLog(@"%@",tips);
    [self showToast:tips];
}

- (IBAction)onExport:(id)sender {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES);
    NSString *desktopPath = [paths firstObject];
    NSLog(@"%@", desktopPath);
    NSString * path = [NSBundle.mainBundle pathForResource:@"EncryptTemplate" ofType:@"txt"];
    NSString *sourceFilePath = path;
    NSString *destinationFilePath = [desktopPath stringByAppendingString:@"/EncryptTemplate.swift"];
    NSError *error;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL success = [fileManager copyItemAtPath:sourceFilePath toPath:destinationFilePath error:&error];
    if (success) {
        [self showToast:@"文件拷贝成功,请在下载中查看EncryptTemplate.swift"];
    } else {
        [self showToast:[NSString stringWithFormat:@"文件拷贝失败，错误信息：%@", error]];
    }

}


-(BOOL)normalOptionCheck {
    if ([self.keyText.stringValue isEqualToString:@""]) {
        [self showToast:@"请输入key"];
        return NO;
    }
    if (self.config.mode == 1 && [self.ivText.stringValue isEqualToString:@""]) {
        [self showToast:@"请输入iv"];
        return NO;
    }
    if (![self checkSelectUrl]){return NO;}
    self.count = 0;
    self.textView.string = @"";
    return YES;
}

// mode: 0 标记, 1.加密 2.恢复 3.恢复带marker
-(void)handleEncryptFiles:(NSString *)sourceCodeDir mode:(NSInteger)mode ignoreDirNames:(NSArray<NSString *> *)ignoreDirNames {
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDirectory;
    if (![fm fileExistsAtPath:sourceCodeDir isDirectory:&isDirectory]) {
        [self showToast:@"找不到该文件"];
        return;
    }
    if (!isDirectory) {
        if ([sourceCodeDir.pathExtension isEqualToString:@"swift"] || [sourceCodeDir.pathExtension isEqualToString:@"m"]) {
            if (mode == 0) {
                [self markEncryptFile:sourceCodeDir];
            } else if (mode == 1){
                [self doEncrypt:sourceCodeDir];
            } else if (mode == 2) {
                [self doDecryptWithMarker:sourceCodeDir needMarker:NO];
            } else if (mode == 3) {
                [self doDecryptWithMarker:sourceCodeDir needMarker:YES];
            }
        }
        return;
    }
    // 遍历源代码文件 h 与 m 配对，swift
    NSArray<NSString *> *files = [fm contentsOfDirectoryAtPath:sourceCodeDir error:nil];
    for (NSString *filePath in files) {
        NSString *path = [sourceCodeDir stringByAppendingPathComponent:filePath];
        if ([fm fileExistsAtPath:path isDirectory:&isDirectory] && isDirectory) {
            if (![ignoreDirNames containsObject:filePath]) {
                [self handleEncryptFiles:path mode:mode ignoreDirNames:ignoreDirNames];
            }
            continue;
        }
        if ([filePath.pathExtension isEqualToString:@"swift"] || [filePath.pathExtension isEqualToString:@"m"]) {
            if (mode == 0) {
                [self markEncryptFile:[sourceCodeDir stringByAppendingPathComponent:filePath]];
            } else if (mode == 1){
                [self doEncrypt:[sourceCodeDir stringByAppendingPathComponent:filePath]];
            } else if (mode == 2) {
                [self doDecryptWithMarker:[sourceCodeDir stringByAppendingPathComponent:filePath] needMarker:NO];
            } else if (mode == 3) {
                [self doDecryptWithMarker:[sourceCodeDir stringByAppendingPathComponent:filePath] needMarker:YES];
            }
        }
    }
}

-(void)markEncryptFile:(NSString*) file {
    if ([file.pathExtension isEqualToString:@"swift"]) {
        [self swiftMark:file];
    } else if ([file.pathExtension isEqualToString:@"m"]) {
        [self ocMark:file];
    }
}

-(void)swiftMark:(NSString*) file {
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDirectory = NO;
    if (![fm fileExistsAtPath:file isDirectory:&isDirectory]) {return;}
    if (isDirectory) {
        return;
    }
    NSError *error = nil;
    NSMutableString *fileContent = [NSMutableString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:&error];
    if (error) {return;}
    
    NSString * regularExpression = self.config.swiftStrMatchRegular;
    NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:regularExpression options:NSRegularExpressionAnchorsMatchLines | NSRegularExpressionUseUnixLineSeparators error:nil];
    NSArray<NSTextCheckingResult *> *matches = [expression matchesInString:fileContent options:0 range:NSMakeRange(0, fileContent.length)];
    NSRange lastRange = NSMakeRange(0, 0);
    NSMutableArray <MatchItem *>* rangeArray =  [NSMutableArray array];
    for (NSTextCheckingResult * obj in matches) {
        if (lastRange.length + lastRange.location > obj.range.location) {
            continue;
        }
        ScanSegment * seg = [Scanner onScan:fileContent withRange:obj.range];
        if (seg) {
            lastRange = seg.range;
            if (!seg.strJoinFlag) {
                MatchItem * item = [MatchItem new];
                item.range = lastRange;
                NSInteger markLength = [self.config.markKey length];
                if (lastRange.location > markLength) {
                    // 向前检查 是否含有关键字mark 防止重复标记
                  NSString * preStr = [fileContent substringWithRange:NSMakeRange(lastRange.location - markLength - 1, markLength + 1)];
                    if ([preStr isEqualToString:[NSString stringWithFormat:@"%@(",self.config.markKey]]) {
                        continue;
                    }
                }
                [rangeArray addObject: item];
            }
        }
//        scanner(fileContent, obj.range, &lastRange, item);
    }
    [self doEncryptMark:rangeArray fileContent:fileContent filePath:file];
}

-(void)ocMark:(NSString*) file {
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDirectory = NO;
    if (![fm fileExistsAtPath:file isDirectory:&isDirectory]) {return;}
    if (isDirectory) {
        return;
    }
    NSError *error = nil;
    NSMutableString *fileContent = [NSMutableString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:&error];
    if (error) {return;}
    NSString * regularExpression = self.config.ocStrMatchRegular;
    NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:regularExpression options:NSRegularExpressionAnchorsMatchLines | NSRegularExpressionUseUnixLineSeparators error:nil];
    NSArray<NSTextCheckingResult *> *matches = [expression matchesInString:fileContent options:0 range:NSMakeRange(0, fileContent.length)];
    __block BOOL isChanged = NO;
    __block NSInteger thisCount = 0;
    [matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSString * matchStr = [fileContent substringWithRange:obj.range];
        if (![matchStr isEqualToString:@"@\"\""]) {
            NSString * markStr = [NSString stringWithFormat:@"MARKER(%@)", matchStr];
            [fileContent replaceCharactersInRange:obj.range withString:markStr];
            isChanged = YES;
            NSString * tips = [NSString stringWithFormat:@"%@标记:%@\n",file,matchStr];
            NSLog(@"%@",tips);
            self.textView.string = [self.textView.string stringByAppendingString:tips];
            thisCount ++;
        }
    }];
    if (isChanged) {
        NSError * error;
        if ([fileContent writeToFile:file atomically:YES encoding:NSUTF8StringEncoding error:&error]) {
            self.count += thisCount;
        } else {
            NSLog(@"%@写入标记后的文件出错",file);
        }
    }
}

-(void)doEncryptMark:(NSMutableArray <MatchItem *>*)rangeArray fileContent:(NSMutableString *)fileContent filePath:(NSString *)filePath {
    BOOL isChanged = NO;
    NSInteger thisCount = 0;
    for (MatchItem * i in rangeArray.reverseObjectEnumerator) {
        NSString * realStr = [fileContent substringWithRange:i.range];
        if (![realStr isEqualToString:@"\"\""]) {
            NSString * markStr = [NSString stringWithFormat:@"MARKER(%@)", realStr];
            [fileContent replaceCharactersInRange:i.range withString:markStr];
            isChanged = YES;
            
            NSString * tips = [NSString stringWithFormat:@"%@标记:%@\n",filePath,realStr];
            NSLog(@"%@",tips);
            self.textView.string = [self.textView.string stringByAppendingString:tips];
            thisCount ++;
        }
    }
    if (isChanged) {
        NSError * error;
        if ([fileContent writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error]) {
            self.count += thisCount;
        } else {
            NSLog(@"%@写入标记后的文件出错",filePath);
        }
    }
}

-(void)doEncrypt:(NSString *)sourceCodeDir {
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDirectory = NO;
    if (![fm fileExistsAtPath:sourceCodeDir isDirectory:&isDirectory]) {return;}
    if (isDirectory) {
        return;
    }
    NSError *error = nil;
    NSMutableString *fileContent = [NSMutableString stringWithContentsOfFile:sourceCodeDir encoding:NSUTF8StringEncoding error:&error];
    if (error) {return;}
    
    BOOL isSwift = [sourceCodeDir.pathExtension isEqualToString:@"swift"];
    
    NSString * regularExpression = isSwift ? [NSString stringWithFormat:@"%@\\(((\".*?\")|(\"\"\"[\\s\\S]*?\"\"\"))\\)", self.config.markKey] : [NSString stringWithFormat:@"%@\\(@\".*?\"\\)", self.config.markKey];
    NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:regularExpression options:NSRegularExpressionAnchorsMatchLines | NSRegularExpressionUseUnixLineSeparators error:nil];
    NSArray<NSTextCheckingResult *> *matches = [expression matchesInString:fileContent options:0 range:NSMakeRange(0, fileContent.length)];
    __block BOOL isChanged = NO;
    [matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (!isChanged) {
            isChanged = YES;
        }
        NSString * matchStr = [fileContent substringWithRange:obj.range];
        NSString * realStr = isSwift ?
        [matchStr substringWithRange:NSMakeRange(8, matchStr.length - 2 - 8)]
        : [matchStr substringWithRange:NSMakeRange(self.config.markKey.length + 3, matchStr.length - 2 - self.config.markKey.length - 3)];
        NSString * enStr;
        if ([realStr isEqualToString: @""]) {
            [fileContent replaceCharactersInRange:obj.range withString:[NSString stringWithFormat:isSwift ? @"\"\"" : @"@\"\""]];
        } else {
            enStr = [StringEncrypt encrypt:realStr key:self.config.AESKey iv:self.config.mode == 0 ? nil : self.config.ivKey];
            [fileContent replaceCharactersInRange:obj.range withString:[NSString stringWithFormat:isSwift ? @"\"%@\".%@()" : @"@\"%@\".%@", enStr, self.config.decryptMethodKey]];
            NSString * tips = [NSString stringWithFormat:@"字符串加密:%@\n", realStr];
            NSLog(@"%@",tips);
            self.count ++;
            self.textView.string = [self.textView.string stringByAppendingString:tips];
        }
    }];
    if (isChanged) {
        [fileContent writeToFile:sourceCodeDir atomically:YES encoding:NSUTF8StringEncoding error:nil];
    }
}

-(void)doDecryptWithMarker:(NSString *)sourceCodeDir needMarker:(BOOL)needMarker {
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDirectory = NO;
    if (![fm fileExistsAtPath:sourceCodeDir isDirectory:&isDirectory]) {return;}
    if (isDirectory) {
        return;
    }
    NSError *error = nil;
    NSMutableString *fileContent = [NSMutableString stringWithContentsOfFile:sourceCodeDir encoding:NSUTF8StringEncoding error:&error];
    if (error) {return;}
    BOOL isSwift = [sourceCodeDir.pathExtension isEqualToString:@"swift"];

    NSString * regularExpression = isSwift ?
    [NSString stringWithFormat:@"\"([^\"\\\\]+)\"\\.%@\\(\\)", self.config.decryptMethodKey]
    : [NSString stringWithFormat:@"@\"([^\"\\\\]+)\"\\.%@", self.config.decryptMethodKey];
    NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:regularExpression options:NSRegularExpressionAnchorsMatchLines | NSRegularExpressionUseUnixLineSeparators error:nil];
    NSArray<NSTextCheckingResult *> *matches = [expression matchesInString:fileContent options:0 range:NSMakeRange(0, fileContent.length)];
    
    
    __block BOOL isChanged = NO;
    [matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (!isChanged) {
            isChanged = YES;
        }
        NSString * realStr = [fileContent substringWithRange:[obj rangeAtIndex:1]];
        NSString * deStr;
        deStr = [StringEncrypt decrypt:realStr key:self.config.AESKey iv:self.config.mode == 0 ? nil : self.config.ivKey];
        NSString * tips = @"";
        if (!deStr || (realStr.length > 0 && [deStr isEqualToString:@""])) {
            // 解密失败
            tips = [NSString stringWithFormat:@"解密失败:%@\n", realStr];
            [fileContent replaceCharactersInRange:obj.range withString:[NSString stringWithFormat:isSwift ? @"\"%@\".%@()" : @"@\"%@\".%@" ,realStr,self.config.markDecryptError]];
        } else {
            tips = [NSString stringWithFormat:@"解密成功:%@\n", deStr];
            if (needMarker) {
                [fileContent replaceCharactersInRange:obj.range withString:[NSString stringWithFormat:isSwift ? @"%@(\"%@\")" : @"%@(@\"%@\")", self.config.markKey,deStr]];
            } else {
                [fileContent replaceCharactersInRange:obj.range withString:[NSString stringWithFormat:isSwift ? @"\"%@\"" : @"@\"%@\"", deStr]];
            }
            self.count ++;
        }
        NSLog(@"%@",tips);
        self.textView.string = [self.textView.string stringByAppendingString:tips];
    }];
    
    if (isChanged) {
        [fileContent writeToFile:sourceCodeDir atomically:YES encoding:NSUTF8StringEncoding error:nil];
    }
}

@end
